from clang.cindex import Cursor, TypeKind, Type


class CTypeFinder:
    type2ctype = {
        # CLANG_TYPE: ()
        TypeKind.VOID: 'None',  # because ctypes.POINTER(None) == c_void_p
        TypeKind.BOOL: 'ct.c_bool',
        TypeKind.CHAR_U: 'ct.c_ubyte',  # ?? used for PADDING
        TypeKind.UCHAR: 'ct.c_ubyte',  # unsigned char
        TypeKind.CHAR16: 'ct.c_wchar',  # char16_t
        TypeKind.CHAR32: 'ct.c_wchar',  # char32_t
        TypeKind.USHORT: 'ct.c_ushort',
        TypeKind.UINT: 'ct.c_uint',
        TypeKind.ULONG: 'ct.c_uint32',
        TypeKind.ULONGLONG: 'ct.c_uint64',
        TypeKind.UINT128: 'ct.c_uint128',
        TypeKind.CHAR_S: 'ct.c_char',  # char
        TypeKind.SCHAR: 'ct.c_byte',  # signed char
        TypeKind.WCHAR: 'ct.c_wchar',
        TypeKind.SHORT: 'ct.c_short',
        TypeKind.INT: 'ct.c_int',
        TypeKind.LONG: 'ct.c_int32',
        TypeKind.LONGLONG: 'ct.c_int64',
        TypeKind.INT128: 'ct.c_int128',
        TypeKind.FLOAT: 'ct.c_float',
        TypeKind.DOUBLE: 'ct.c_double',
        TypeKind.LONGDOUBLE: 'ct.c_longdouble',
        TypeKind.NULLPTR: 'ct.c_void_p'
    }

    def find_declaration(self, T: Type) -> Cursor | list[Cursor] | None:
        """找到该类型涉及到的所有依赖类型声明"""
        kind = T.kind
        if kind in self.type2ctype:
            # 原子定义终止
            return None
        meth = getattr(self, kind.name, None)
        if meth:
            return meth(T)
        else:
            return T.get_declaration()

    def TYPEDEF(self, T: Type):
        d = T.get_canonical()
        return self.find_declaration(d)

    def CONSTANTARRAY(self, T: Type):
        return self.find_declaration(T.element_type)

    def INCOMPLETEARRAY(self, T: Type):
        return self.find_declaration(T.element_type)

    def POINTER(self, T: Type):
        pointer = T.get_pointee()
        return self.find_declaration(pointer)

    def ENUM(self, T: Type):
        return self.find_declaration(T.get_declaration().enum_type)

    def ELABORATED(self, T: Type):
        return self.find_declaration(T.get_canonical())

    def FUNCTIONPROTO(self, T: Type):
        ret = [self.find_declaration(T.get_result())]
        for arg in T.argument_types():
            ret.append(self.find_declaration(arg))
        return ret

    def FUNCTIONNOPROTO(self, T: Type):
        ret = [self.find_declaration(T.get_result())]
        return ret
